#include <algorithm>
#include <iostream>  
#include <iterator>
#include <queue>
#include <string>  
#include <vector>  
#include <nlohmann/json.hpp>  
#include <sw/redis++/redis++.h>
  
using namespace std;  
using json = nlohmann::json;  
  
// Trie节点结构  
class TrieNode 
{  
public:  
    // 指向子节点的指针数组（这里只考虑小写字母a-z）  
    TrieNode* children[26];  
    // 标记该节点是否是某个单词的结尾  
    bool isEndOfWord;  
  
    // 构造函数  
    TrieNode() 
    {  
        isEndOfWord = false;  
        for (int i = 0; i < 26; i++) 
        {  
            children[i] = nullptr;  
        }  
    }  
};  
  
// Trie类  
class Trie 
{  
private:  
    TrieNode* root; 

private:
    // 构造函数  
    Trie() 
    {  
        root = new TrieNode();  
    }  

public:  
    ~Trie() = default;  
  
    // 禁止拷贝构造和赋值操作  
    Trie(const Trie&) = delete;  
    Trie& operator=(const Trie&) = delete;  
  
    // 提供一个全局访问点来获取实例  
    static Trie& getInstance() {  
        static Trie instance; // 局部静态变量，在函数第一次被调用时初始化  
        return instance;  
    }   

public:  
  
    // 插入一个单词到Trie中  
    void insert(const string& word) 
    {  
        TrieNode* node = root;  
        for (char c : word) 
        {  
            int index = c - 'a';  
            if (!node->children[index])
            {  
                node->children[index] = new TrieNode();  
            }  
            node = node->children[index];  
        }  
        node->isEndOfWord = true;  
    }  
  
    // 搜索Trie中是否存在某个单词  
    bool search(const string& word) 
    {  
        TrieNode* node = searchPrefix(word);  
        return node != nullptr && node->isEndOfWord;  
    }  
  
    // 搜索Trie中是否存在某个单词的前缀  
    TrieNode* searchPrefix(const string& word) 
    {  
        TrieNode* node = root;  
        for (char c : word) 
        {  
            int index = c - 'a';  
            if (!node->children[index]) 
            {  
                return nullptr;  
            }  
            node = node->children[index];  
        }  
        return node;  
    }  
  
    // 以某个前缀开头的单词的数量  
    int startsWith(const string& prefix) 
    {  
        TrieNode* node = searchPrefix(prefix);  
        if (!node) 
            return 0;  
  
        int count = 0;  
        countNodes(node, count);  
        return count;  
    }  
  
    // 辅助函数，用于计算以某个节点为根的子树中单词的数量  
    void countNodes(TrieNode* node, int& count) 
    {  
        if (!node) 
            return;  
        if (node->isEndOfWord) count++;  
        for (int i = 0; i < 26; i++) 
        {  
            if (node->children[i]) 
            {  
                countNodes(node->children[i], count);  
            }  
        }  
    }

    struct cmp { bool operator()(const pair<string, int>& p1, const pair<string, int>& p2){ return p1.second > p2.second; }};
    // 根据一个前缀进行匹配
    void collectPrefixMatches(TrieNode* node, const string& prefix, string current
    , priority_queue<pair<string, int>, vector<pair<string, int>>, cmp>& results) 
    {  
        if (!node) 
            return;  
    
        // 如果当前节点是某个单词的结尾，则添加该单词到结果中  
        if (node->isEndOfWord) 
        {  
            results.push({ prefix + current, gethotrate(prefix + current) });
            if (results.size() > 5) 
            {
                results.pop();
                return;
            }
        }  
    
        // 遍历所有子节点  
        for (int i = 0; i < 26; ++i) 
        {  
            if (node->children[i]) 
            {  
                char c = 'a' + i;
                collectPrefixMatches(node->children[i], prefix, current + c, results);             
            }  
        }  
    }  
    
    // 将结果转换为JSON字符串  
    string getPrefixMatchesJson(const string& prefix) 
    {
        priority_queue<pair<string, int>, vector<pair<string, int>>, cmp> heap;
        vector<string> results;  
        TrieNode* node = root;
        json j;  
        // 先把前缀进行匹配，匹配后再说
        for(auto& e : prefix)
        {
            // 如果判断出来node是空，就意味着匹配不到东西了
            if(node == nullptr)
            {
                j["matches"] = results;
                return j.dump();
            }
            node = node->children[e - 'a'];
        }
        // 运行到这里，就意味着前缀匹配完了，剩下后缀就随便匹配
        collectPrefixMatches(node, prefix, "", heap);
        while(!heap.empty())
        {
            results.push_back(heap.top().first);
            heap.pop();
        }
        // 把结果逆序出来，就是按照频率排序的结果
        reverse(results.begin(), results.end());
        j["matches"] = results;  
        return j.dump();  
    }

    int gethotrate(const string& word)
    {
        // 可能会需要Docker进行部署，所以更新一个带Docker的解决方式
        const char* redisenv = getenv("REDIS_NAME");
        string redisconn = "tcp://";
        if(redisenv == nullptr)
            redisconn = redisconn + "127.0.0.1" + ":6379";
        else   
            redisconn = redisconn + redisenv + ":6379";
        
        sw::redis::Redis redis(redisconn);

        // 去Redis里面获取结果
        auto result = redis.zscore("users:hotwords", word);
        if(result.has_value())
        {
            return result.value();
        }
        else
        {
            return 0;
        }
    }
};
